using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MongoDB.Driver;
using Sustainalytics.Entities;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.Synchronization.Validation.Internals;
using Sustainalytics.Synchronization.Validation.ValidationRules.Common;

namespace Sustainalytics.Synchronization.Validation.Restore
{
    public class HasCompanyIdProfileRestoreHandler<T> : AbstractProfileRestoreHandler<T>
        where T : IHasCompanyId, IEntity
    {
        public ILogger _logger;

        public HasCompanyIdProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections)
        {
            _logger = logger;
        }

        /// <summary>
        /// The for each elements restore the to the old entities.
        /// </summary>
        /// <param name="invalidCompanies">
        ///     The invalid Companies to have the companies restored.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="invalidCompanies"/> is <see langword="null"/>.
        /// </exception>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        public override async Task ForEachElementsAsync(InvalidCompaniesList invalidCompanies)
        {
            if (invalidCompanies == null)
            {
                throw new ArgumentNullException("invalidCompanies");
            }

            var companyIds = invalidCompanies.GetCompanies(ResearchProduct);

            if (!companyIds.Any())
            {
                return;
            }

            if (ValidatedDataCollection == null)
                throw new ArgumentNullException("ValidatedDataCollection", "validated data collection is null.");

            _logger.Log($"Performing restore profile for {ResearchProduct} companies.");
            _logger.Log($"{companyIds.Count} companies will be restored.");

            await ValidatedDataCollection.DeleteManyAsync(GetEntityFilter(companyIds)).ConfigureAwait(false);

            var oldEntities = await GetReferenceElementsAsync(companyIds).ConfigureAwait(false);

            await GetInvalidNewEntitiesAsync(oldEntities, companyIds, x => x.CompanyId).ConfigureAwait(false);

            if (!oldEntities.Any())
            {
                return;
            }

            foreach (var entity in oldEntities)
            {
                entity.Id = Guid.NewGuid();
            }

            await ValidatedDataCollection.InsertManyAsync(oldEntities).ConfigureAwait(false);
        }


        protected override FilterDefinition<T> GetEntityFilter(IEnumerable<Guid> companyIds)
        {
            return Builders<T>.Filter.In(x => x.CompanyId, companyIds);
        }
    }

    public class ComprehensiveHasCompanyIdProfileRestoreHandler<T> : HasCompanyIdProfileRestoreHandler<T>
        where T : IHasCompanyId, IEntity
    {
        public ComprehensiveHasCompanyIdProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Comprehensive;
        }
    }

    public class CoreHasCompanyIdProfileRestoreHandler<T> : HasCompanyIdProfileRestoreHandler<T>
        where T : IHasCompanyId, IEntity
    {
        public CoreHasCompanyIdProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Core;
        }
    }

    public class GovernanceHasCompanyIdProfileRestoreHandler<T> : HasCompanyIdProfileRestoreHandler<T>
        where T : IHasCompanyId, IEntity
    {
        public GovernanceHasCompanyIdProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.CorporateGovernance;
        }
    }

    public class EventsHasCompanyIdProfileRestoreHandler<T> : HasCompanyIdProfileRestoreHandler<T>
       where T : IHasCompanyId, IEntity
    {
        public EventsHasCompanyIdProfileRestoreHandler(ValidationMongoCollection<T> collections, ILogger logger)
            : base(collections, logger)
        {
            ResearchProduct = BusinessResearchProduct.Events;
        }
    }
}